/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//import HexUtil from '../utils/HexUtil.ets';
import TextUtils from './TextUtils';

class UrlBeaconUrlCompressor {
//  private static readonly EDDYSTONE_URL_REGEX="^((?i)http|https):\/\/((?i)www\.)?((?:[0-9a-zA-Z_-]+\.?)+)(/?)([./0-9a-zA-Z_-]*)";
  private static readonly EDDYSTONE_URL_PROTOCOL_GROUP: number= 1;
  private static readonly EDDYSTONE_URL_WWW_GROUP: number= 2;
  private static readonly EDDYSTONE_URL_FQDN_GROUP: number= 3;
  private static readonly EDDYSTONE_URL_SLASH_GROUP: number= 4;
  private static readonly EDDYSTONE_URL_PATH_GROUP: number= 5;
  private static readonly URL_PROTOCOL_HTTP_WWW_DOT: string= "http://www.";
  private static readonly URL_PROTOCOL_HTTPS_WWW_DOT: string= "https://www.";
  private static readonly URL_PROTOCOL_HTTP: string= "http";
  private static readonly URL_PROTOCOL_HTTP_COLON_SLASH_SLASH: string= "http://";
  private static readonly URL_PROTOCOL_HTTPS_COLON_SLASH_SLASH: string= "https://";
  private static readonly URL_HOST_WWW: string= "www.";
  private static readonly URL_TLD_DOT_COM: string= ".com";
  private static readonly URL_TLD_DOT_ORG: string= ".org";
  private static readonly URL_TLD_DOT_EDU: string= ".edu";
  private static readonly URL_TLD_DOT_NET: string= ".net";
  private static readonly URL_TLD_DOT_INFO: string= ".info";
  private static readonly URL_TLD_DOT_BIZ: string= ".biz";
  private static readonly URL_TLD_DOT_GOV: string= ".gov";
  private static readonly URL_TLD_DOT_COM_SLASH: string= ".com/";
  private static readonly URL_TLD_DOT_ORG_SLASH: string = ".org/";
  private static readonly URL_TLD_DOT_EDU_SLASH: string = ".edu/";
  private static readonly URL_TLD_DOT_NET_SLASH: string = ".net/";
  private static readonly URL_TLD_DOT_INFO_SLASH: string = ".info/";
  private static readonly URL_TLD_DOT_BIZ_SLASH: string = ".biz/";
  private static readonly URL_TLD_DOT_GOV_SLASH: string = ".gov/";
  private static readonly EDDYSTONE_URL_PROTOCOL_HTTP_WWW: number  = 0x00;
  private static readonly EDDYSTONE_URL_PROTOCOL_HTTPS_WWW: number = 0x01;
  private static readonly EDDYSTONE_URL_PROTOCOL_HTTP: number      = 0x02;
  private static readonly EDDYSTONE_URL_PROTOCOL_HTTPS: number     = 0x03;
  private static readonly EDDYSTONE_URL_COM_SLASH: number  = 0x00;
  private static readonly EDDYSTONE_URL_ORG_SLASH: number   = 0x01;
  private static readonly EDDYSTONE_URL_EDU_SLASH: number   = 0x02;
  private static readonly EDDYSTONE_URL_NET_SLASH: number   = 0x03;
  private static readonly EDDYSTONE_URL_INFO_SLASH: number  = 0x04;
  private static readonly EDDYSTONE_URL_BIZ_SLASH: number   = 0x05;
  private static readonly EDDYSTONE_URL_GOV_SLASH: number   = 0x06;
  private static readonly EDDYSTONE_URL_COM: number         = 0x07;
  private static readonly EDDYSTONE_URL_ORG: number         = 0x08;
  private static readonly EDDYSTONE_URL_EDU: number         = 0x09;
  private static readonly EDDYSTONE_URL_NET: number         = 0x0a;
  private static readonly EDDYSTONE_URL_INFO: number        = 0x0b;
  private static readonly EDDYSTONE_URL_BIZ: number         = 0x0c;
  private static readonly EDDYSTONE_URL_GOV: number         = 0x0d;
  private static readonly TLD_NOT_ENCODABLE: number        = -1;
  private static tldMap: Array<TLDMapEntry>

  public static settldMap(): void{
    if (this.tldMap == null) {
      this.tldMap = new Array();
    }
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_COM_SLASH, this.EDDYSTONE_URL_COM_SLASH));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_ORG_SLASH, this.EDDYSTONE_URL_ORG_SLASH));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_EDU_SLASH, this.EDDYSTONE_URL_EDU_SLASH));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_NET_SLASH, this.EDDYSTONE_URL_NET_SLASH));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_INFO_SLASH, this.EDDYSTONE_URL_INFO_SLASH));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_BIZ_SLASH, this.EDDYSTONE_URL_BIZ_SLASH));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_GOV_SLASH, this.EDDYSTONE_URL_GOV_SLASH));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_COM, this.EDDYSTONE_URL_COM));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_ORG, this.EDDYSTONE_URL_ORG));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_EDU, this.EDDYSTONE_URL_EDU));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_NET, this.EDDYSTONE_URL_NET));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_INFO, this.EDDYSTONE_URL_INFO));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_BIZ, this.EDDYSTONE_URL_BIZ));
    this.tldMap.push(new TLDMapEntry(this.URL_TLD_DOT_GOV, this.EDDYSTONE_URL_GOV));
  }

  private static encodedByteForTopLevelDomain(tld: string): number{
    let encodedByte: number = this.TLD_NOT_ENCODABLE;
    this.settldMap();
    let tldFound: boolean = false;
    if (!tldFound && this.tldMap) {
      for (var i = 0; i < this.tldMap.length; i++) {
        var entry: TLDMapEntry = this.tldMap[i];
        tldFound = (entry.tld == tld) ? true : false;
        if (tldFound) {
          encodedByte = entry.encodedByte;
        }
      }
    }
    return encodedByte;
  }

  private static topLevelDomainForByte(encodedByte: number): string{
    var tld: string = null;
    this.settldMap();
    var tldFound: boolean = false;
    if (!tldFound && this.tldMap) {
      for (var i = 0;i < this.tldMap.length; i++) {
        var entry: TLDMapEntry = this.tldMap[i];
        tldFound = entry.encodedByte == encodedByte;
        if (tldFound) {
          tld = entry.tld;
        }
      }
    }
    return tld;
  }

  public static compress(urlString: string): Array<number>{
    var compressedBytes: Array<number> = new Array();
    if (urlString != null) {
      var byteBuffer: Array<number> = new Array<number>(urlString.length);
      var byteBufferIndex: number = 0;
      for (var i = 0; i < byteBuffer.length; i++) {
        byteBuffer[i] = 0x00;
      }
      let urlPattern = /^(http|https):\/\/(www\.)?((?:[0-9a-zA-Z_-]+\.?)+)(\/?)([./0-9a-zA-Z_-]*)/i;
      if (urlPattern.test(urlString)) {
        var wwwdot: string = urlString.match(urlPattern)[this.EDDYSTONE_URL_WWW_GROUP];
        var haswww: boolean = (wwwdot != null);

        var rawProtocol: string = urlString.match(urlPattern)[this.EDDYSTONE_URL_PROTOCOL_GROUP];
        var protocol: string = rawProtocol.toLowerCase();
        if (protocol == this.URL_PROTOCOL_HTTP.toLowerCase()) {
          byteBuffer[byteBufferIndex] = (haswww ? this.EDDYSTONE_URL_PROTOCOL_HTTP_WWW : this.EDDYSTONE_URL_PROTOCOL_HTTP);
        } else {
          byteBuffer[byteBufferIndex] = (haswww ? this.EDDYSTONE_URL_PROTOCOL_HTTPS_WWW : this.EDDYSTONE_URL_PROTOCOL_HTTPS);
        }
        byteBufferIndex++;


        var hostnameBytes: Array<number> = TextUtils.stringToBytes(
          urlString.match(urlPattern)[this.EDDYSTONE_URL_FQDN_GROUP]);
        var rawHostname: string = (urlString.match(urlPattern)[this.EDDYSTONE_URL_FQDN_GROUP]).toString();
        var hostname = rawHostname.toLowerCase();
        var domains: Array<string> = hostname.split(".");


        var consumedSlash: boolean = false;
        if (domains != null) {
          var periodBytes: Array<number> = TextUtils.stringToBytes(".");
          var writableDomainsCount: number = (domains.length == 1 ? 1 : domains.length - 1);
          for (var domainIndex = 0; domainIndex < writableDomainsCount; domainIndex++) {
            if (domainIndex > 0) {
              byteBuffer = periodBytes;
              byteBufferIndex += periodBytes.length;
            }

            var domainBytes: Array<number> = TextUtils.stringToBytes(domains[domainIndex]);
            var domainLength: number = domainBytes.length;
            byteBuffer.splice(1, domainLength);
            for (var i = 0; i < domainBytes.length; i++) {
              var DB = domainBytes[i];
              byteBuffer.splice(byteBufferIndex, 0, DB);
            }
            byteBufferIndex += domainLength;
          }

          if (domains.length > 1) {
            var tld: string = "." + domains[domains.length - 1];
            var slash: string = urlString.match(urlString)[this.EDDYSTONE_URL_SLASH_GROUP];
            var encodableTLDCandidate: string = (slash == null ? tld : tld + slash);
            var encodedTLDByte: number = UrlBeaconUrlCompressor.encodedByteForTopLevelDomain(encodableTLDCandidate);
            if (encodedTLDByte != this.TLD_NOT_ENCODABLE) {
              byteBuffer[byteBufferIndex++] = encodedTLDByte;
              consumedSlash = (slash != null);
            } else {
              var tldBytes: Array<number> = TextUtils.stringToBytes(tld);
              var tldLength: number = tldBytes.length;
              byteBuffer = tldBytes;
              byteBufferIndex += tldLength;
            }
          }
        }

        if (!consumedSlash) {
          var slash: string = urlString.match(urlString)[this.EDDYSTONE_URL_SLASH_GROUP];
          if (slash != null) {
            var slashLength: number = slash.length;
            byteBuffer = TextUtils.stringToBytes(slash);
            byteBufferIndex += slashLength;
          }
        }

        var path: string = urlString.match(urlString)[this.EDDYSTONE_URL_PATH_GROUP];
        if (path != null) {
          var pathLength: number = path.length;
          byteBuffer = TextUtils.stringToBytes(path);
          byteBufferIndex += pathLength;
        }

        compressedBytes = new Array();
        byteBuffer = compressedBytes;
      }
    }
    return compressedBytes;
  }

  public static uncompress(compressedURL: Array<number>): string{
    var url: string;
    switch (compressedURL[0] & 0x0f) {
      case this.EDDYSTONE_URL_PROTOCOL_HTTP_WWW:
        url.concat(this.URL_PROTOCOL_HTTP_WWW_DOT);
        break;
      case this.EDDYSTONE_URL_PROTOCOL_HTTPS_WWW:
        url.concat(this.URL_PROTOCOL_HTTPS_WWW_DOT);
        break;
      case this.EDDYSTONE_URL_PROTOCOL_HTTP:
        url.concat(this.URL_PROTOCOL_HTTP_COLON_SLASH_SLASH);
        break;
      case this.EDDYSTONE_URL_PROTOCOL_HTTPS:
        url.concat(this.URL_PROTOCOL_HTTPS_COLON_SLASH_SLASH);
        break;
      default:
        break;
    }
    var lastByte = -1;
    for (var i = 0; i < compressedURL.length; i++) {
      var b = compressedURL[i];
      if (lastByte == 0 && b == 0) {
        break;
      }
      lastByte = b;
      var tld: string = UrlBeaconUrlCompressor.topLevelDomainForByte(b);
      if (tld != null) {
        url.concat(tld);
      } else {
        url.concat(b.toString());
      }
    }
    return url.toString();
  }
}

class TLDMapEntry {
  public tld: string;
  public encodedByte: number;

  constructor(topLevelDomain: string, encodedTLDByte: number) {
    this.tld = topLevelDomain;
    this.encodedByte = encodedTLDByte;
  }
}

export default UrlBeaconUrlCompressor;